第二章有第二章自己的 repo: ch2,裡面有一個資料下載腳本,主要是來自於一假想公司 AcmeContent 的影片觀看資料,dwell-time
是網站停留時間長度。
上個單元,我們有使用過 incanter.excel/load-xls
。這裡將使用 incanter.io/read-dataset
來讀取 tab-seperated file。
(defn load-data [file]
(-> (io/resource file)
(io/read-dataset :header true :delim \tab)))
(defn ex-2-1 []
(-> (load-data "dwell-times.tsv")
(i/view)))
可以通過 Day 06 介紹的繪圖函數 c/histogram
簡單的看資料分布狀況:
(defn ex-2-2 []
(-> (i/$ :dwell-time (load-data "dwell-times.tsv"))
(c/histogram :x-label "Dwell time (s)"
:nbins 50)
(i/view)))
如圖,這不是一個常態分布。在展示這樣的資料上,將 Y 軸數據取 log 是不錯的方法。很多的數據,例如地震或噪音,其實也都是通過將數值資料取對數的方式來表示。我們可以使用 c/log-axis
這個函數來修正 Y 軸數據。
(defn ex-2-3 []
(-> (i/$ :dwell-time (load-data "dwell-times.tsv"))
(c/histogram :x-label "Dwell time (s)"
:nbins 20)
(c/set-axis :y (c/log-axis :label "Log Frequency"))
(i/view)))
若只有一軸取對數,此圖稱為 log-linear,若 X、Y 軸都取對數,則稱呼為 log-log 圖。通過繪圖可以看到 dwell-time 跟頻率的對數有限性關係。
首先來看看指數分布的平均值跟標準差。
(defn ex-2-4 []
(let [dwell-times (->> (load-data "dwell-times.tsv")
(i/$ :dwell-time))]
(println "Mean: " (s/mean dwell-times))
(println "Median: " (s/median dwell-times))
(println "SD: " (s/sd dwell-times))))
;; Mean: 93.2014074074074
;; Median: 64.0
;; SD: 93.96972402519796
在完美的指數分布中,平均跟標準差會是相同的!另一個指數分布的特色是無記憶性(memoryless),也就是某個狀況下的機率大小(例如:觀眾繼續觀看影片的機率),並不會因為過去的情況變化而有所改變(例如:已經看了多久影片)。以地震的例子,也就是說,即便在過去的兩百年都沒發生地震,明年地震的機率,跟去年有地震發生的明年地震的機率是相等的。
若要深入探索資料,勢必要能處理日期、時間相關的資料。在原始資料中,日期、時間是以字串形式儲存,必須先轉換成 Java 的資料結構!這裡使用 clj-time
。這個庫允許我們:
clj-time.parse
clj-time.predicates
接著我們就可以建構函數了:
(defn with-parsed-date [data]
(i/transform-col data :date (comp tc/to-local-date f/parse)))
(defn filter-weekdays [data]
(i/$where {:date {:$fn p/weekdays?}} data))
(defn mean-dwell-times-by-date [data]
(i/$rollup :mean :dwelltime :date data))
(defn daily-mean-dwell-times [data]
(->> (with-parsed-date data)
(filter-weekdays)
(mean-dwell-times-by-date)))
with-parsed-date
:將日期字串轉化為日期資料結構filter-weekdays
:輸入一個日期資料結構,輸出一個 true
false
列表mean-dwell-times-by-datge
:通過列表將為 true
的行取出做比較計算一下周間的影片觀看狀況:
(defn ex-2-5 []
(let [means (->> (load-data "dwell-times.tsv")
(daily-mean-dwell-times)
(i/$ :dwell-time))]
(println "Mean: " (s/mean means))
(println "Median: " (s/median means))
(println "SD: " (s/sd means))))
;; Mean: 90.210428650562
;; Median: 90.13661202185791
;; SD: 3.722342905320035
發現 SD 小了很多。為什麼呢?
(defn ex-2-6 []
(let [means (->> (load-data "dwell-times.tsv")
(daily-mean-dwell-times)
(i/$ :dwell-times))]
(-> (c/histogram means
:x-label "Daily mean dwell time (s)"
:nbins 20)
(i/view))))
結果竟然是近似常態分佈的狀況:以 90 秒左右為軸,向左右以標準差 3.7 秒分布。